﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://wpfimageviewer.codeplex.com
using System;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using BrainTechLLC;
using System.Windows.Threading;
using System.Windows;
using BrainTechLLC.ThreadSafeObjects;
using System.Threading;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Serialization;
using System.Dynamic;
using Expression = System.Linq.Expressions.Expression;

namespace BrainTechLLC.WPF
{
    public class CustomMetaObject : DynamicMetaObject
    {
        public CustomMetaObject(Expression parameter, object value) : base(parameter, BindingRestrictions.Empty, value) { }

        public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
        {
            var self = this.Expression;
            Type type = base.Value.GetType();

            UnaryExpression unarySet = Expression.Convert(self, type);
            Expression target;

            if (unarySet.Method == null && typeof(ViewModelBase).IsAssignableFrom(type))
            {
                ViewModelBase vm = base.Value as ViewModelBase;
                MethodInfo mi = type.GetMethod("DynamicSetValue", BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.FlattenHierarchy
                var propertyNameExpression = Expression.Constant(binder.Name);
                var thisVarExpression = Expression.Constant(base.Value);
                var valueExpression = Expression.Convert(value.Expression, typeof(object));
                target = Expression.Call(Expression.Convert(self, type), mi, thisVarExpression, propertyNameExpression, valueExpression);
            }
            else if (unarySet.Method == null && typeof(MultiViewModelBase).IsAssignableFrom(type))
            {
                MultiViewModelBase vm = base.Value as MultiViewModelBase;
                MethodInfo mi = type.GetMethod("DynamicSetValue", BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.FlattenHierarchy
                var propertyNameExpression = Expression.Constant(binder.Name);
                var thisVarExpression = Expression.Constant(base.Value);
                var valueExpression = Expression.Convert(value.Expression, typeof(object));
                target = Expression.Call(Expression.Convert(self, type), mi, thisVarExpression, propertyNameExpression, valueExpression);
            }
            else
            {
                target = Expression.Call(unarySet, null);
            }

            var restrictions = BindingRestrictions.GetInstanceRestriction(self, base.Value);
            return new DynamicMetaObject(target, restrictions);
        }

        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
        {
            Expression self = this.Expression;
            Type type = base.Value.GetType();

            UnaryExpression unaryGet = Expression.Convert(self, type);
            Expression target;

            if (unaryGet.Method == null && typeof(ViewModelBase).IsAssignableFrom(type))
            {
                ViewModelBase vm = base.Value as ViewModelBase;
                MethodInfo mi = type.GetMethod("GetValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
                var propertyNameExpression = Expression.Constant(binder.Name);
                var thisVarExpression = Expression.Constant(base.Value);
                target = Expression.Call(Expression.Convert(self, type), mi, thisVarExpression, propertyNameExpression);
            }
            else if (unaryGet.Method == null && typeof(MultiViewModelBase).IsAssignableFrom(type))
            {
                MultiViewModelBase vm = base.Value as MultiViewModelBase;
                MethodInfo mi = type.GetMethod("GetValue", BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
                var propertyNameExpression = Expression.Constant(binder.Name);
                var thisVarExpression = Expression.Constant(base.Value);
                target = Expression.Call(Expression.Convert(self, type), mi, thisVarExpression, propertyNameExpression);
            }
            else
            {
                target = Expression.Call(unaryGet, null);
            }

            var restrictions = BindingRestrictions.GetInstanceRestriction(self, base.Value);
            return new DynamicMetaObject(target, restrictions);
        }
    }


}
